Skip to content

20260105 #3 spring security 기본 설정#4

Merged
Chuseok22 merged 45 commits intomainfrom
20260105_#3_Spring_Security_기본_설정
Jan 12, 2026

Hidden character warning

The head ref may contain hidden characters: "20260105_#3_Spring_Security_\uae30\ubcf8_\uc124\uc815"
Merged

20260105 #3 spring security 기본 설정#4
Chuseok22 merged 45 commits intomainfrom
20260105_#3_Spring_Security_기본_설정

Conversation

@Chuseok22
Copy link
Member

@Chuseok22 Chuseok22 commented Jan 12, 2026

✨ 변경 사항


✅ 테스트


  • 수동 테스트 완료
  • 테스트 코드 완료

Summary by CodeRabbit

  • 새 기능

    • JWT 액세스/리프레시 발급·검증, 리프레시 토큰 Redis 저장, 토큰 유틸 및 보안 상수 추가
    • 사용자 어댑터·인증 필터·멤버 조회 서비스 및 멤버 엔티티/리포지토리 도입
    • 전역 예외 처리, CORS 설정, JPA 감사 및 시간 제공자 추가
  • 개선 사항

    • 보안 필터 기반 인증/권한 검사와 경로별 접근 제어 도입
  • 기타

    • 빌드 설정·라이브러리(로깅/JWT/Redis) 및 모듈 구성/이미지/워크플로우 업데이트

✏️ Tip: You can customize this high-level summary in your review settings.

@Chuseok22 Chuseok22 self-assigned this Jan 12, 2026
@Chuseok22 Chuseok22 added the feat 새로운 기능 추가 (Feature) label Jan 12, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 12, 2026

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'ignored_branch'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

JWT 기반 인증·인가 도입, Redis 리프레시 토큰 저장소 추가, Spring Security 필터·설정 및 사용자 어댑터/예외·시간·베이스 엔티티와 Redis 모듈·빌드/버전 카탈로그 변경이 포함된 기능 추가 및 구성 변경입니다.

Changes

코호트 / 파일(s) 변경 요약
빌드·버전 카탈로그 & 설정
gradle/libs.versions.toml, settings.gradle.kts, CT-auth/build.gradle.kts, CT-common/build.gradle.kts, CT-redis/build.gradle.kts, Dockerfile, .github/workflows/spring-boot-cicd.yml
버전·라이브러리 추가(jjwt, kotlin-logging), 의존성 추가/조정(CT-member, CT-redis, jjwt, sejong portal), CT-redis 모듈 포함, Docker COPY 경로 및 워크플로 트리거 수정
토큰 인터페이스·스토어
CT-auth/src/main/kotlin/.../core/token/TokenProvider.kt, .../TokenStore.kt
TokenProvider, TokenStore 인터페이스 추가(토큰 생성·검증·저장 API 정의)
JWT 구현·스토어·설정
CT-auth/.../infrastructure/jwt/JwtProvider.kt, .../JwtStore.kt, .../properties/JwtProperties.kt, .../config/JwtConfig.kt
JJWT 기반 JwtProvider 구현(토큰 생성/검증/클레임 추출), Redis 기반 JwtStore 구현, JwtProperties 바인딩 및 SecretKey/JwtProvider/JwtStore 빈 등록
보안 필터·설정
CT-auth/.../infrastructure/filter/TokenAuthenticationFilter.kt, .../config/SecurityConfig.kt
TokenAuthenticationFilter 추가(화이트리스트, 토큰 추출·검증, 권한 검사, 에러 응답), SecurityFilterChain 및 필터 빈 등록
상수·화이트리스트·유틸
CT-auth/.../infrastructure/constant/AuthConstants.kt, .../constant/SecurityUrls.kt, .../util/AuthUtil.kt
인증 관련 상수, 인증 제외 URL 화이트리스트, 요청에서 토큰 추출 및 Redis 키 생성 유틸 추가
사용자 어댑터·서비스
CT-auth/.../core/user/UserPrincipal.kt, .../infrastructure/user/CustomUserDetails.kt, .../CustomUserDetailsService.kt, CT-member/.../application/MemberService.kt
UserPrincipal 인터페이스, Member 기반 CustomUserDetails, UserDetailsService 구현, Member 조회 서비스 추가
회원 도메인·리포지토리·역할
CT-member/.../infrastructure/entity/Member.kt, .../infrastructure/repository/MemberRepository.kt, .../core/constant/Role.kt
Member JPA 엔티티(팩토리·정규화·soft-delete 포함), 리포지토리 쿼리 메서드, Role enum 추가
공통 예외·응답 DTO
CT-common/.../application/exception/CustomException.kt, .../ErrorCode.kt, .../ErrorResponse.kt
CustomException, ErrorCode(enum), ErrorResponse DTO 추가(전역 예외 처리 연계)
시간 추상화·JPA 베이스
CT-common/.../core/time/TimeProvider.kt, .../infrastructure/time/SystemTimeProvider.kt, .../infrastructure/config/TimeConfig.kt, .../infrastructure/persistence/BaseEntity.kt
TimeProvider 추상화 및 구현, TimeConfig 빈, JPA 감사 필드·soft-delete가 포함된 BaseEntity 추가
Redis 구성·프로퍼티
CT-redis/.../infrastructure/config/RedisConfig.kt, .../infrastructure/properties/RedisProperties.kt
LettuceConnectionFactory, RedisTemplate(GenericJackson2JsonRedisSerializer) 설정 및 RedisProperties 바인딩 추가
웹 설정·예외 처리·CORS·스캔·Swagger
CT-web/.../CampusTableServerApplication.kt, .../application/exception/GlobalExceptionHandler.kt, .../infrastructure/config/CorsConfig.kt, .../infrastructure/config/ComponentScanConfig.kt, .../infrastructure/config/SwaggerConfig.kt
@EnableJpaAuditing 추가, 전역 예외 핸들러, CORS 설정, ComponentScan 경로 조정, Swagger bean 이름 변경

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Filter as TokenAuthenticationFilter
    participant Provider as JwtProvider
    participant MemberSvc as MemberService
    participant Repo as MemberRepository
    participant SecurityCtx as SecurityContext

    Client->>Filter: 요청(Authorization 헤더 포함)
    Filter->>Filter: 화이트리스트 검사
    alt 화이트리스트 경로
        Filter-->>Client: 인증 없이 요청 계속
    else 보호 경로
        Filter->>Filter: 토큰 추출 및 포맷 검사
        Filter->>Provider: isValidToken(token)
        Provider-->>Filter: 유효/무효 (만료 예외 가능)
        alt 유효
            Filter->>Provider: getMemberId(token)
            Provider-->>Filter: memberId
            Filter->>MemberSvc: findMemberById(memberId)
            MemberSvc->>Repo: findByIdAndDeletedFalse(memberId)
            Repo-->>MemberSvc: Member
            MemberSvc-->>Filter: Member
            Filter->>Filter: CustomUserDetails 생성 및 권한 검사
            Filter->>SecurityCtx: Authentication 설정
            Filter-->>Client: 요청 처리 계속
        else 무효/만료
            Filter-->>Client: 에러 응답(예: 401/권한 오류)
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 토끼가 말하네—
비밀키로 꿈을 잇고, 토큰 길을 닦아,
헤더 한 줄이면 문이 열리고,
Redis 굴속에 리프레시 숨겨두고,
작은 발로 안전을 지키네.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 39.34% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 변경 사항의 주요 내용과 부분적으로 관련됨. 'Spring Security 기본 설정'은 실제 변경의 일부이지만, JWT 인증, Redis 통합, 공통 예외 처리 등 더 광범위한 변경을 모두 포함하지는 않음.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 113df9b and b6bf452.

📒 Files selected for processing (1)
  • Dockerfile
🔇 Additional comments (1)
Dockerfile (1)

10-10: CT-web 모듈 JAR 경로 변경이 올바릅니다.

Gradle 멀티 모듈 구조에서 CT-web 모듈의 Spring Boot 빌드 출력 경로(CT-web/build/libs/CT-web-*.jar)가 정확하게 지정되었습니다. 버전은 루트 build.gradle.kts에서 정의된 0.1.0으로 설정되어 있으며, 와일드카드 패턴이 버전 변경에 유연하게 대응합니다. Dockerfile의 COPY 명령어가 올바른 위치의 JAR 파일을 /app.jar로 복사하고 있습니다.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🤖 Fix all issues with AI agents
In @CT-auth/build.gradle.kts:
- Around line 20-21: The build declares the test-only dependency using the
runtime API configuration (api(libs.spring.security.test)), which can leak test
libs into production classpath; change that declaration to a test-scoped
configuration such as testImplementation(libs.spring.security.test) (or testApi
if the test dependency must be exposed to downstream test modules) so
spring.security.test is only included on the test classpath.
- Around line 23-27: Update the jjwt dependency to the latest 0.13.0 release by
replacing the current reference to libs.jjwt in the dependencies block (the line
with "api(libs.jjwt)") so the project uses jjwt 0.13.0; after changing the
dependency reference, refresh/resolve Gradle dependencies and run a full
build/test to verify there are no compatibility issues with classes or JWT usage
in the codebase.

In
@CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/filter/TokenAuthenticationFilter.kt:
- Around line 64-74: The current try/catch in TokenAuthenticationFilter catches
only CustomException and ExpiredJwtException, leaving other exceptions
unhandled; add a final catch (e: Exception) block after the existing catches in
the authenticate/filters' try scope to SecurityContextHolder.clearContext(), log
the exception with log.error(e) including a clear message like
"[TokenAuthenticationFilter] Unexpected error", and call
sendErrorResponse(response, ErrorCode.INTERNAL_SERVER_ERROR) (or the appropriate
generic ErrorCode) to return a controlled error response instead of letting
exceptions bubble up the filter chain.

In
@CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/jwt/JwtProvider.kt:
- Around line 61-69: getMemberId() is reading a non-existent "memberId" claim
but createToken() stores the memberId as the JWT subject; change getMemberId()
to retrieve the subject from the parsed claims (e.g., claims.subject or
claims.getSubject()) and throw CustomException(ErrorCode.INVALID_JWT) if subject
is null/blank, and apply the same fix to the other affected method(s) around
lines 80-94 that similarly expect a "memberId" claim instead of the subject;
preserve existing JwtException logging/throwing behavior.

In
@CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/user/CustomUserDetails.kt:
- Around line 21-23: getMemberId currently calls member.id.toString() which can
NPE because member.id is UUID?; make the call null-safe (e.g. use
member.id?.toString() ?: "") or throw a clear IllegalStateException if an id
must exist, and update the CustomUserDetails.getMemberId implementation
accordingly so it never dereferences a nullable UUID without a check.

In
@CT-common/src/main/kotlin/com/chuseok22/ctcommon/application/exception/ErrorCode.kt:
- Line 20: Update the enum entry MEMBER_NOT_FOUND in ErrorCode (the ErrorCode.kt
enum) to fix the message typo: replace the current message "회원을 없습니다" with the
correct phrase "회원을 찾을 수 없습니다" so the MEMBER_NOT_FOUND constant returns the
grammatically correct Korean error message.

In
@CT-member/src/main/kotlin/com/chuseok22/ctmember/infrastructure/repository/MemberRepository.kt:
- Around line 7-11: The repository method names in MemberRepository reference a
non-existent 'deleted' field; update the method names to match the entity's
soft-delete field 'isDeleted' so Spring Data JPA generates correct queries:
rename findByIdAndDeletedFalse(...) to findByIdAndIsDeletedFalse(...) and
findByStudentNameAndDeletedFalse(...) to
findByStudentNameAndIsDeletedFalse(...). Ensure the signatures (parameter types
and return types) remain unchanged while only renaming the methods.

In
@CT-redis/src/main/kotlin/com/chuseok22/ctredis/infrastructure/config/RedisConfig.kt:
- Around line 82-87: Current polymorphic typing uses
BasicPolymorphicTypeValidator.builder().allowIfBaseType(Any::class.java) which
permits deserialization of any type and is a security risk; replace this with a
restricted validator that only allows known safe packages or explicit classes
(e.g., use
BasicPolymorphicTypeValidator.builder().allowIfSubType("com.yourapp.package").allowIfSubType("com.yourapp.dto").or
explicitly allow specific classes) and pass that validator into
activateDefaultTyping instead of allowIfBaseType(Any::class.java) to limit
accepted polymorphic types for
activateDefaultTyping/ObjectMapper.DefaultTyping.NON_FINAL.

In
@CT-web/src/main/kotlin/com/chuseok22/ctweb/application/exception/GlobalExceptionHandler.kt:
- Around line 37-49: The handleException method currently logs only the
exception message; update it to pass the exception object to the logger so the
full stack trace is recorded (mirror how handleCustomException logs the
exception). Locate the handleException function and change the log.error call to
include the throwable (e.g., log.error(e) or log.error(e) { "예상치 못한 예외 발생:
${e.message}" }) so the stack trace is emitted while keeping the existing
response creation intact.

In @gradle/libs.versions.toml:
- Around line 48-49: Replace the legacy aggregator entry `jjwt = { module =
"io.jsonwebtoken:jjwt", version.ref = "jjwt" }` with separate modular entries
`jjwt-api`, `jjwt-impl`, and a serializer like `jjwt-jackson` in
libs.versions.toml (using the same version.ref), and update build.gradle.kts to
use `implementation(libs.jjwt.api)` and `runtimeOnly(libs.jjwt.impl)` plus
`runtimeOnly(libs.jjwt.jackson)` so the API, implementation and JSON mapper are
declared with correct scopes.
🧹 Nitpick comments (14)
CT-web/src/main/kotlin/com/chuseok22/ctweb/CampusTableServerApplication.kt (1)

7-9: @EnableJpaAuditing을 별도의 설정 클래스로 분리하는 것을 권장합니다.

메인 애플리케이션 클래스에 @EnableJpaAuditing을 직접 추가하면 @DataJpaTest 슬라이스 테스트 시 문제가 발생할 수 있습니다. 테스트 컨텍스트에서 auditing 관련 빈이 로드되지 않아 테스트가 실패할 수 있습니다.

♻️ 별도 설정 클래스로 분리하는 방법

새 파일 생성: CT-web/src/main/kotlin/com/chuseok22/ctweb/config/JpaAuditingConfig.kt

package com.chuseok22.ctweb.config

import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaAuditing

@Configuration
@EnableJpaAuditing
class JpaAuditingConfig

메인 애플리케이션 클래스에서는 @EnableJpaAuditing 제거:

 package com.chuseok22.ctweb

 import org.springframework.boot.autoconfigure.SpringBootApplication
 import org.springframework.boot.runApplication
-import org.springframework.data.jpa.repository.config.EnableJpaAuditing

 @SpringBootApplication
-@EnableJpaAuditing
 class CampusTableServerApplication

이렇게 분리하면 테스트 시 @Import(JpaAuditingConfig::class)로 필요할 때만 포함하거나, @DataJpaTest에서 제외할 수 있습니다.

CT-common/src/main/kotlin/com/chuseok22/ctcommon/infrastructure/persistence/BaseEntity.kt (1)

44-47: delete() 메서드 구현이 적절합니다.

Instant를 파라미터로 받아 테스트 용이성을 확보한 점이 좋습니다. AI 요약에서 언급된 TimeProvider와 잘 연동될 것으로 보입니다.

향후 soft delete 복원 기능이 필요할 경우를 대비하여 restore() 메서드 추가를 고려해 보세요.

♻️ 선택적 개선안
  fun delete(now: Instant) {
    this.isDeleted = true
    deletedAt = now
  }
+
+  /**
+   * Soft Delete 복원
+   */
+  fun restore() {
+    this.isDeleted = false
+    this.deletedAt = null
+  }
CT-redis/src/main/kotlin/com/chuseok22/ctredis/infrastructure/properties/RedisProperties.kt (1)

6-13: @Validated 사용 시 유효성 검증 어노테이션 추가 고려

@Validated 어노테이션을 사용하고 있지만, 실제 유효성 검증 어노테이션(@NotBlank, @Min 등)이 프로퍼티에 적용되어 있지 않습니다. 또한, Kotlin data class에서 빈 클래스 블록 {}은 생략 가능합니다.

♻️ 제안 코드
 @Validated
 @ConfigurationProperties(prefix = "spring.data.redis")
 data class RedisProperties(
+  @field:NotBlank
   val host: String,
+  @field:Min(1)
   val port: Int,
   val password: String
-) {
-}
+)

jakarta.validation.constraints.NotBlankjakarta.validation.constraints.Min import가 필요합니다.

CT-web/src/main/kotlin/com/chuseok22/ctweb/infrastructure/config/CorsConfig.kt (1)

14-23: Origin 패턴 외부화 고려

CORS 설정은 적절하게 구성되어 있습니다. 다만, allowedOriginPatterns가 하드코딩되어 있어 환경별로 다른 도메인을 사용해야 할 경우 재배포가 필요합니다. 환경 변수나 application.yml을 통한 외부화를 고려해 볼 수 있습니다.

♻️ 외부화 예시
// CorsProperties.kt
@ConfigurationProperties(prefix = "cors")
data class CorsProperties(
  val allowedOriginPatterns: List<String>
)

// CorsConfig.kt
@Configuration
@EnableConfigurationProperties(CorsProperties::class)
class CorsConfig(
  private val corsProperties: CorsProperties
) {
  @Bean
  fun corsConfigurationSource(): CorsConfigurationSource {
    val configuration = CorsConfiguration().apply {
      allowedOriginPatterns = corsProperties.allowedOriginPatterns
      // ... 나머지 설정
    }
    // ...
  }
}
# application.yml
cors:
  allowed-origin-patterns:
    - "https://www.campustable.shop"
    - "http://localhost:3000"
CT-common/src/main/kotlin/com/chuseok22/ctcommon/application/exception/CustomException.kt (1)

3-5: LGTM! 커스텀 예외 클래스가 깔끔하게 구현되었습니다.

ErrorCode를 활용한 예외 처리 패턴이 적절합니다. 필요시 예외 체이닝을 위해 cause 파라미터를 추가하는 것도 고려해볼 수 있습니다.

💡 예외 체이닝 지원 (선택사항)
 class CustomException(
   val errorCode: ErrorCode,
-) : RuntimeException(errorCode.message)
+  cause: Throwable? = null
+) : RuntimeException(errorCode.message, cause)
CT-common/src/main/kotlin/com/chuseok22/ctcommon/application/exception/ErrorResponse.kt (1)

3-6: LGTM! 에러 응답 구조가 적절합니다.

일관된 에러 응답을 위한 data class가 잘 정의되어 있습니다.

JSON 직렬화 시 errorCode가 enum 이름 그대로 노출됩니다. 클라이언트에 더 명확한 형태로 전달하려면 커스텀 직렬화를 고려해볼 수 있습니다:

💡 JSON 직렬화 형식 개선 (선택사항)
data class ErrorResponse(
  val errorCode: String,  // ErrorCode.name 또는 code 값
  val errorMessage: String
) {
  companion object {
    fun of(errorCode: ErrorCode) = ErrorResponse(
      errorCode = errorCode.name,
      errorMessage = errorCode.message
    )
  }
}
CT-auth/src/main/kotlin/com/chuseok22/ctauth/core/token/TokenStore.kt (1)

3-13: LGTM! 깔끔한 인터페이스 설계입니다.

토큰 저장소 추상화가 잘 정의되어 있습니다.

향후 토큰 재발급(reissue) 로직에서 저장된 리프레시 토큰을 조회해야 할 경우, get(key: String): String? 메서드 추가를 고려해 보세요.

/**
 * Key에 해당하는 리프레시 토큰 조회
 */
fun get(key: String): String?
CT-member/src/main/kotlin/com/chuseok22/ctmember/application/MemberService.kt (1)

12-12: 사용되지 않는 로거 변수

log 변수가 선언되었지만 클래스 내에서 사용되지 않습니다. 향후 로깅이 필요할 경우를 대비한 것으로 보이나, 현재는 불필요한 코드입니다.

🔧 제안: 사용하지 않을 경우 제거
-private val log = KotlinLogging.logger { }
-
 @Service
 class MemberService(
CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/config/JwtConfig.kt (1)

24-28: Secret Key 최소 길이 검증 권장

HMAC-SHA 알고리즘에서 보안을 위해 secret key는 최소 256비트(32바이트) 이상이어야 합니다. 잘못된 길이의 키가 설정될 경우 런타임 예외가 발생할 수 있으므로, 초기화 시점에 키 길이를 검증하는 것이 좋습니다.

🔒 제안: Secret Key 길이 검증 추가
 @Bean
 fun jwtSecretKey(): SecretKey {
   val keyBytes: ByteArray = Decoders.BASE64.decode(properties.secretKey)
+  require(keyBytes.size >= 32) { "JWT secret key must be at least 256 bits (32 bytes)" }
   return Keys.hmacShaKeyFor(keyBytes)
 }
CT-common/src/main/kotlin/com/chuseok22/ctcommon/application/exception/ErrorCode.kt (1)

10-20: 메시지 구두점 일관성 권장

에러 메시지들의 구두점 사용이 일관되지 않습니다. 일부는 마침표로 끝나고("접근이 거부되었습니다."), 일부는 마침표가 없습니다. 통일된 스타일을 적용하는 것이 좋습니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/properties/JwtProperties.kt (1)

12-13: 만료 시간 값에 양수 검증 추가를 권장합니다.

accessExpMillisrefreshExpMillis가 0 또는 음수일 경우 토큰이 즉시 만료되거나 예상치 못한 동작이 발생할 수 있습니다. @field:Positive 어노테이션을 추가하여 설정 오류를 방지하세요.

♻️ 권장 수정사항
 import jakarta.validation.constraints.NotBlank
+import jakarta.validation.constraints.Positive
 import org.springframework.boot.context.properties.ConfigurationProperties
 import org.springframework.validation.annotation.Validated

 @Validated
 @ConfigurationProperties(prefix = "jwt")
 data class JwtProperties(
   @field:NotBlank
   val secretKey: String,
+  @field:Positive
   val accessExpMillis: Long,
+  @field:Positive
   val refreshExpMillis: Long,
   @field:NotBlank
   val issuer: String
 )
CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/config/SecurityConfig.kt (1)

28-30: CORS 설정이 기본값으로만 구성됨

.cors {} 는 빈 설정으로, 명시적인 CORS 정책이 없습니다. 프로덕션 환경에서는 허용할 origin, method, header를 명시적으로 설정하는 것이 권장됩니다.

♻️ 명시적 CORS 설정 예시
@Bean
fun corsConfigurationSource(): CorsConfigurationSource {
  val configuration = CorsConfiguration().apply {
    allowedOrigins = listOf("https://campustable.shop")
    allowedMethods = listOf("GET", "POST", "PUT", "DELETE", "OPTIONS")
    allowedHeaders = listOf("*")
    allowCredentials = true
  }
  return UrlBasedCorsConfigurationSource().apply {
    registerCorsConfiguration("/**", configuration)
  }
}

그리고 filterChain에서:

-.cors {}
+.cors { it.configurationSource(corsConfigurationSource()) }
CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/constant/AuthConstants.kt (1)

9-10: ROOT_DOMAIN을 환경 설정으로 외부화 고려

도메인을 하드코딩하면 개발/스테이징/프로덕션 환경 간 전환이 어렵습니다. @ConfigurationProperties를 통해 외부 설정으로 관리하는 것이 좋습니다.

CT-member/src/main/kotlin/com/chuseok22/ctmember/infrastructure/entity/Member.kt (1)

8-14: JPA 엔티티의 equals()/hashCode() 구현 고려

UUID 기반 엔티티에서 equals()hashCode()를 오버라이드하지 않으면 영속성 컨텍스트 외부에서 컬렉션 사용 시 예상치 못한 동작이 발생할 수 있습니다. id 기반으로 구현을 추가하는 것이 권장됩니다.

♻️ equals/hashCode 구현 예시
override fun equals(other: Any?): Boolean {
  if (this === other) return true
  if (other !is Member) return false
  return id != null && id == other.id
}

override fun hashCode(): Int = javaClass.hashCode()
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f7bc437 and 343279d.

📒 Files selected for processing (37)
  • CT-auth/build.gradle.kts
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/core/token/TokenProvider.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/core/token/TokenStore.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/core/user/UserPrincipal.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/config/JwtConfig.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/config/SecurityConfig.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/constant/AuthConstants.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/constant/SecurityUrls.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/filter/TokenAuthenticationFilter.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/jwt/JwtProvider.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/jwt/JwtStore.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/properties/JwtProperties.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/user/CustomUserDetails.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/user/CustomUserDetailsService.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/util/AuthUtil.kt
  • CT-common/build.gradle.kts
  • CT-common/src/main/kotlin/com/chuseok22/ctcommon/application/exception/CustomException.kt
  • CT-common/src/main/kotlin/com/chuseok22/ctcommon/application/exception/ErrorCode.kt
  • CT-common/src/main/kotlin/com/chuseok22/ctcommon/application/exception/ErrorResponse.kt
  • CT-common/src/main/kotlin/com/chuseok22/ctcommon/core/time/TimeProvider.kt
  • CT-common/src/main/kotlin/com/chuseok22/ctcommon/infrastructure/config/TimeConfig.kt
  • CT-common/src/main/kotlin/com/chuseok22/ctcommon/infrastructure/persistence/BaseEntity.kt
  • CT-common/src/main/kotlin/com/chuseok22/ctcommon/infrastructure/time/SystemTimeProvider.kt
  • CT-member/src/main/kotlin/com/chuseok22/ctmember/application/MemberService.kt
  • CT-member/src/main/kotlin/com/chuseok22/ctmember/core/constant/Role.kt
  • CT-member/src/main/kotlin/com/chuseok22/ctmember/infrastructure/entity/Member.kt
  • CT-member/src/main/kotlin/com/chuseok22/ctmember/infrastructure/repository/MemberRepository.kt
  • CT-redis/build.gradle.kts
  • CT-redis/src/main/kotlin/com/chuseok22/ctredis/infrastructure/config/RedisConfig.kt
  • CT-redis/src/main/kotlin/com/chuseok22/ctredis/infrastructure/properties/RedisProperties.kt
  • CT-web/src/main/kotlin/com/chuseok22/ctweb/CampusTableServerApplication.kt
  • CT-web/src/main/kotlin/com/chuseok22/ctweb/application/exception/GlobalExceptionHandler.kt
  • CT-web/src/main/kotlin/com/chuseok22/ctweb/infrastructure/config/ComponentScanConfig.kt
  • CT-web/src/main/kotlin/com/chuseok22/ctweb/infrastructure/config/CorsConfig.kt
  • CT-web/src/main/kotlin/com/chuseok22/ctweb/infrastructure/config/SwaggerConfig.kt
  • gradle/libs.versions.toml
  • settings.gradle.kts
🧰 Additional context used
🪛 detekt (1.23.8)
CT-redis/src/main/kotlin/com/chuseok22/ctredis/infrastructure/properties/RedisProperties.kt

[warning] 12-13: The class or object RedisProperties is empty.

(detekt.empty-blocks.EmptyClassBlock)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (31)
CT-common/src/main/kotlin/com/chuseok22/ctcommon/core/time/TimeProvider.kt (1)

5-8: LGTM!

테스트 가능한 시간 추상화를 위한 깔끔한 인터페이스입니다. Instant를 반환 타입으로 사용한 것은 타임존 독립적인 시간 처리에 적합합니다. core 패키지에 인터페이스를 배치하고 구현체를 infrastructure에 분리한 구조도 좋습니다.

CT-common/src/main/kotlin/com/chuseok22/ctcommon/infrastructure/time/SystemTimeProvider.kt (1)

7-13: LGTM!

Clock을 주입받아 테스트 시 시간을 제어할 수 있는 좋은 구현입니다. TimeConfig에서 명시적으로 빈을 생성하므로 @Component 어노테이션 없이도 정상 동작합니다.

CT-common/src/main/kotlin/com/chuseok22/ctcommon/infrastructure/config/TimeConfig.kt (1)

9-21: LGTM!

UTC 기반 Clock 빈과 TimeProvider 빈이 올바르게 구성되어 있습니다. 서버 환경에서 Clock.systemUTC()를 사용하는 것은 타임존 혼란을 방지하는 좋은 선택입니다. 테스트 시에는 Clock.fixed()를 주입하여 시간을 고정할 수 있어 단위 테스트 작성이 용이합니다.

CT-common/src/main/kotlin/com/chuseok22/ctcommon/infrastructure/persistence/BaseEntity.kt (3)

11-12: 클래스 구조 LGTM!

@MappedSuperclass@EntityListeners(AuditingEntityListener::class) 조합으로 Spring Data JPA 감사(auditing) 기능이 올바르게 구성되었습니다.


29-39: Soft delete 필드 구성 LGTM!

isDeleted 필드는 non-null로, deletedAt 필드는 nullable로 적절하게 구성되었습니다. 삭제되지 않은 엔티티에서 deletedAt이 null인 것이 자연스러운 설계입니다.


18-27: 감사(auditing) 필드 구현이 올바릅니다.

@field: 접두사를 사용하여 Kotlin에서 JPA 어노테이션이 backing field에 적용되도록 올바르게 처리했습니다. Instant? = null 타입과 nullable = false 조합은 Spring Data JPA auditing에서 표준 패턴입니다. TIMESTAMPTZ는 프로젝트에서 PostgreSQL을 유일한 대상 데이터베이스로 사용하고 있기 때문에 적절합니다.

CT-common/build.gradle.kts (1)

28-30: LGTM!

kotlin-logging 의존성을 api 스코프로 추가한 것은 적절합니다. 이를 통해 CT-common에 의존하는 모든 모듈에서 일관된 로깅 유틸리티를 사용할 수 있습니다.

settings.gradle.kts (1)

6-6: LGTM!

Redis 모듈을 프로젝트에 포함시키는 것은 PR 목적에 부합합니다.

CT-web/src/main/kotlin/com/chuseok22/ctweb/infrastructure/config/ComponentScanConfig.kt (1)

7-9: LGTM!

basePackages"com.chuseok22"으로 설정하면 해당 패키지와 모든 하위 패키지(ctauth, ctredis, ctweb 등)가 스캔됩니다. 멀티 모듈 프로젝트에서 올바른 설정입니다.

CT-member/src/main/kotlin/com/chuseok22/ctmember/core/constant/Role.kt (1)

3-6: LGTM! Spring Security 권한 명명 규칙을 올바르게 따르고 있습니다.

ROLE_ 접두사를 사용한 enum 정의가 Spring Security의 기본 권한 체계와 호환됩니다.

CT-web/src/main/kotlin/com/chuseok22/ctweb/infrastructure/config/SwaggerConfig.kt (1)

27-28: LGTM!

메서드 이름이 OpenAPI()에서 openAPI()로 변경되어 Kotlin/Java의 camelCase 네이밍 컨벤션을 따르게 되었습니다. 적절한 리팩토링입니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/constant/SecurityUrls.kt (1)

3-17: LGTM! 보안 화이트리스트가 잘 정의되어 있습니다.

인증 및 Swagger 문서 엔드포인트가 적절히 포함되어 있습니다.

향후 Actuator 헬스체크 엔드포인트(/actuator/health) 등 추가 공개 엔드포인트가 필요할 수 있는지 확인해 주세요. 현재 설정으로는 해당 엔드포인트들도 인증이 필요합니다.

CT-redis/build.gradle.kts (1)

1-19: LGTM!

라이브러리 모듈 구성이 올바르게 설정되어 있습니다. bootJar 비활성화와 jar 활성화 패턴이 적절하며, spring-boot-starter-data-redisapi 스코프로 선언하여 이 모듈을 의존하는 다른 모듈에서 Redis 의존성을 transitively 사용할 수 있도록 했습니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/user/CustomUserDetailsService.kt (1)

16-21: JWT 토큰과 사용자 조회 흐름 확인 완료 - 일관성 문제 없음

JWT 토큰의 subject는 memberId (UUID)를 사용하므로, username 파라미터로 studentName 조회하는 것은 일관성이 있습니다. Spring Security의 UserDetailsService 인터페이스 제약상 메서드명은 loadUserByUsername이어야 하지만, 이 도메인에서 "username"은 studentName을 의미하므로 문제없습니다. 구현이 올바르고 findByStudentNameAndDeletedFalse 메서드도 정상 작동합니다.

CT-member/src/main/kotlin/com/chuseok22/ctmember/application/MemberService.kt (1)

19-23: LGTM!

@Transactional(readOnly = true) 적용과 soft-delete 필터링을 통한 멤버 조회 로직이 적절합니다. 멤버가 없을 경우 CustomException을 던지는 패턴도 일관성 있게 구현되었습니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/jwt/JwtStore.kt (1)

7-16: LGTM!

TokenStore 인터페이스 구현이 적절합니다. Redis를 통한 refresh token 저장 및 TTL 관리가 올바르게 구현되었습니다. Bean 등록은 JwtConfig에서 처리하므로 @Component 어노테이션 없이 작동합니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/config/JwtConfig.kt (1)

33-44: LGTM!

JwtProviderJwtStore Bean 등록이 적절하게 구성되었습니다. 의존성 주입 방식이 명확하고 테스트하기 용이한 구조입니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/core/user/UserPrincipal.kt (1)

3-18: LGTM!

UserPrincipal 인터페이스가 인증 주체의 핵심 정보를 명확하게 정의하고 있습니다. CustomUserDetails에서 이 인터페이스를 구현하여 Spring Security와 통합하는 구조가 적절합니다.

getMemberId()String을 반환하는 이유가 확인되었습니다. Member 엔티티의 idUUID 타입이며, CustomUserDetails에서 member.id.toString()으로 명시적으로 변환하고 있습니다. 이는 REST API와 JSON 직렬화 호환성을 위한 의도적인 설계이므로 현재 구현이 적절합니다. UUID 반환 타입이 특별히 필요하지 않은 한, 현재 구현은 유지해도 됩니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/core/token/TokenProvider.kt (1)

5-31: LGTM!

인터페이스 설계가 깔끔하고 토큰 생성, 검증, 파싱에 필요한 핵심 메서드들이 잘 정의되어 있습니다. 구현체와의 분리를 통해 테스트 용이성과 확장성을 확보했습니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/util/AuthUtil.kt (1)

6-27: LGTM!

유틸리티 클래스가 깔끔하게 구현되었습니다. Kotlin의 null-safe 연산자(?., takeIf)를 적절히 활용하여 Bearer 토큰 추출 로직이 안전하게 처리됩니다. 빈 문자열과 공백 처리도 잘 되어 있습니다.

CT-web/src/main/kotlin/com/chuseok22/ctweb/application/exception/GlobalExceptionHandler.kt (1)

14-32: CustomException 핸들러 구현이 적절합니다.

예외 객체와 함께 로깅하여 스택 트레이스가 포함되고, errorCode의 HTTP 상태와 메시지를 올바르게 활용하고 있습니다.

gradle/libs.versions.toml (1)

3-3: Kotlin 2.3.0는 2025년 12월 16일에 JetBrains에서 공식 릴리스했으며, Spring Boot 3.5.9는 Kotlin 1.7.x 이상을 지원하므로 호환성에 문제가 없습니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/user/CustomUserDetails.kt (1)

10-36: 전체적인 구현이 적절합니다.

JWT 기반 인증에서 getPassword()가 빈 문자열을 반환하는 것은 적절하며, Spring Security 6+에서는 isAccountNonExpired(), isAccountNonLocked() 등의 기본 구현이 true를 반환하므로 별도 오버라이드가 필요 없습니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/config/SecurityConfig.kt (1)

26-52: SecurityFilterChain 구성이 적절합니다.

CSRF 비활성화, stateless 세션 정책, JWT 필터 추가 등 JWT 기반 인증에 적합한 구성입니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/constant/AuthConstants.kt (1)

1-23: 상수 구성이 체계적입니다.

인증 관련 상수들이 용도별로 잘 분류되어 있으며, 코드 전반에서 일관되게 사용될 수 있습니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/filter/TokenAuthenticationFilter.kt (2)

87-97: ApiRequestType.OTHER에 대한 처리 명확화 필요

정의되지 않은 URI 타입에 대해 경고 로그만 남기고 계속 진행합니다. 의도한 동작인지 확인이 필요합니다. 보안상 알 수 없는 경로는 차단하는 것이 안전할 수 있습니다.


30-75: 전체적인 필터 구조가 적절합니다.

화이트리스트 체크, 토큰 검증, 인증 컨텍스트 설정 흐름이 잘 구성되어 있습니다.

CT-redis/src/main/kotlin/com/chuseok22/ctredis/infrastructure/config/RedisConfig.kt (1)

28-38: Redis 연결 설정이 적절합니다.

Lettuce 기반 standalone 구성이 잘 되어 있습니다. 프로덕션에서는 연결 풀링이나 Sentinel/Cluster 구성을 고려해볼 수 있습니다.

CT-member/src/main/kotlin/com/chuseok22/ctmember/infrastructure/entity/Member.kt (1)

35-51: 팩토리 메서드 패턴 적용이 잘 되어 있습니다.

create() 팩토리 메서드와 private 생성자를 통해 객체 생성을 캡슐화하고, 입력값 정규화/검증을 일관되게 적용하고 있습니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/jwt/JwtProvider.kt (2)

22-36: 토큰 생성 로직이 적절합니다.

액세스 토큰과 리프레시 토큰을 카테고리로 구분하고, 만료 시간을 properties에서 관리하는 구조가 잘 되어 있습니다.


38-59: 토큰 검증 예외 처리가 잘 구성되어 있습니다.

만료된 토큰은 재전달하고, 다른 유효하지 않은 토큰 유형들은 적절히 로깅하며 false를 반환합니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
CT-common/src/main/kotlin/com/chuseok22/ctcommon/infrastructure/persistence/BaseEntity.kt (1)

44-47: delete() 메서드에 멱등성(idempotency) 검사 추가를 고려해 주세요.

이미 삭제된 엔티티에 대해 delete()를 다시 호출하면 기존 deletedAt 타임스탬프가 덮어씌워집니다. 감사(audit) 추적 관점에서 최초 삭제 시점을 보존하는 것이 중요할 수 있습니다.

♻️ 멱등성 검사를 추가한 수정 제안
  fun delete(now: Instant) {
+   if (deleted) return
    this.deleted = true
    deletedAt = now
  }

또는 이미 삭제된 경우 예외를 던지도록 할 수도 있습니다:

  fun delete(now: Instant) {
+   require(!deleted) { "이미 삭제된 엔티티입니다." }
    this.deleted = true
    deletedAt = now
  }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 343279d and 33c93ca.

📒 Files selected for processing (1)
  • CT-common/src/main/kotlin/com/chuseok22/ctcommon/infrastructure/persistence/BaseEntity.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
CT-common/src/main/kotlin/com/chuseok22/ctcommon/infrastructure/persistence/BaseEntity.kt (2)

11-13: LGTM! 표준 Spring Data JPA 감사(auditing) 설정입니다.

@MappedSuperclassAuditingEntityListener 조합이 올바르게 구성되었습니다. AI 요약에 따르면 CampusTableServerApplication@EnableJpaAuditing이 활성화되어 있어 정상 동작할 것입니다.


18-27: PostgreSQL 특화 컬럼 타입 사용 확인이 필요합니다.

TIMESTAMPTZ는 PostgreSQL 전용 컬럼 타입입니다. 프로젝트가 PostgreSQL만 사용한다면 문제없지만, 다른 데이터베이스로의 마이그레이션이 필요할 경우 호환성 문제가 발생할 수 있습니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
@CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/filter/TokenAuthenticationFilter.kt:
- Around line 107-124: Wrap the UUID parsing inside handleValidToken: after
obtaining memberId via tokenProvider.getMemberId(token), catch
IllegalArgumentException around UUID.fromString(memberId) and convert it to a
clear authentication failure (e.g., throw new BadCredentialsException or
AuthenticationServiceException with a descriptive message) so the error is
explicit instead of propagating a raw IllegalArgumentException; ensure you do
not call memberService.findMemberById or proceed to filterChain.doFilter when
parsing fails.

In @gradle/libs.versions.toml:
- Around line 3-8: The Kotlin version entry (kotlin = "2.3.0") is not known to
be compatible with Spring Boot 3.5.9, so either pin Kotlin to a Spring
Boot–compatible version or upgrade Spring Boot to a 4.x line that explicitly
supports Kotlin 2.3; update the kotlin key accordingly and run full compile/test
matrix; also bump jjwt from "0.12.7" to "0.13.0" by changing the jjwt key, and
leave kotlinLogging as-is ("7.0.14") unless tests surface issues.
🧹 Nitpick comments (1)
CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/jwt/JwtProvider.kt (1)

71-78: getExpiredAt에서 Exception 대신 구체적인 예외 타입을 사용하는 것을 고려하세요.

모든 Exception을 catch하면 예상치 못한 오류(예: NullPointerException, OutOfMemoryError 등)도 INVALID_JWT로 변환될 수 있습니다. JwtException만 catch하는 것이 더 정확할 수 있습니다.

♻️ 권장 수정
 override fun getExpiredAt(token: String): Date {
   return try {
     getClaims(token).expiration
-  } catch (e: Exception) {
+  } catch (e: JwtException) {
     log.error(e) { "JWT 만료시간 추출 실패: ${e.message}" }
     throw CustomException(ErrorCode.INVALID_JWT)
   }
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 33c93ca and 113df9b.

📒 Files selected for processing (8)
  • .github/workflows/spring-boot-cicd.yml
  • CT-auth/build.gradle.kts
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/filter/TokenAuthenticationFilter.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/jwt/JwtProvider.kt
  • CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/user/CustomUserDetails.kt
  • CT-common/src/main/kotlin/com/chuseok22/ctcommon/application/exception/ErrorCode.kt
  • CT-redis/src/main/kotlin/com/chuseok22/ctredis/infrastructure/config/RedisConfig.kt
  • gradle/libs.versions.toml
🚧 Files skipped from review as they are similar to previous changes (2)
  • CT-redis/src/main/kotlin/com/chuseok22/ctredis/infrastructure/config/RedisConfig.kt
  • CT-common/src/main/kotlin/com/chuseok22/ctcommon/application/exception/ErrorCode.kt
🔇 Additional comments (9)
.github/workflows/spring-boot-cicd.yml (1)

4-6: LGTM!

main 브랜치에 대한 CI/CD 트리거 활성화가 적절합니다. 이 변경으로 main 브랜치에 푸시될 때 워크플로우가 실행됩니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/user/CustomUserDetails.kt (1)

10-37: LGTM! JWT 기반 인증을 위한 UserDetails 구현이 적절합니다.

getPassword()가 빈 문자열을 반환하는 것은 JWT 기반 인증에서 적절하며, getMemberId()의 null 체크와 예외 처리도 잘 구현되어 있습니다.

한 가지 참고 사항: getName()member.name을, getUsername()member.studentName을 반환하는데, 두 필드의 의미 차이가 명확하다면 문제없지만, 추후 혼동을 방지하기 위해 문서화를 고려해 보세요.

CT-auth/build.gradle.kts (1)

14-30: LGTM! 의존성 구성이 적절합니다.

JWT 라이브러리 구성(jjwt-apiimplementation, jjwt-impljjwt-jacksonruntimeOnly)이 JJWT 권장 패턴을 따르고 있습니다. spring-security-testapi에서 implementation으로 변경한 것도 테스트 의존성이 외부로 노출되지 않도록 하는 올바른 선택입니다.

gradle/libs.versions.toml (1)

48-54: LGTM! JJWT 및 Kotlin Logging 라이브러리 선언이 적절합니다.

JJWT 라이브러리(jjwt-api, jjwt-impl, jjwt-jackson)와 Kotlin Logging 라이브러리가 올바르게 구성되어 있습니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/filter/TokenAuthenticationFilter.kt (2)

38-80: 필터 로직 구조가 적절합니다.

화이트리스트 체크 후 토큰 검증, 예외 처리 분기가 명확합니다. ExpiredJwtException을 별도로 처리하여 만료된 토큰에 대한 명확한 에러 코드를 반환하는 것이 좋습니다.


150-163: 에러 응답 처리가 잘 구현되어 있습니다.

ObjectMapper를 사용한 JSON 응답 생성과 적절한 Content-Type, 상태 코드, 인코딩 설정이 포함되어 있습니다.

CT-auth/src/main/kotlin/com/chuseok22/ctauth/infrastructure/jwt/JwtProvider.kt (3)

61-69: getMemberId의 예외 처리가 개선될 수 있습니다.

JwtException을 catch한 후 로깅하고 다시 throw하는 패턴은 유효하지만, ExpiredJwtExceptionJwtException의 하위 클래스이므로 여기서 catch되어 원래의 예외 유형이 유지됩니다. 현재 구현이 의도된 동작이라면 문제없습니다.


80-103: LGTM! JWT 토큰 생성 및 검증 로직이 잘 구현되어 있습니다.

Instant.now()를 사용한 시간 처리, claim 설정, 서명 로직이 JJWT 라이브러리의 권장 패턴을 따르고 있습니다. parseSignedClaims()를 사용한 검증도 적절합니다.


38-59: 토큰 검증 로직이 적절합니다.

ExpiredJwtException을 rethrow하여 호출자가 만료된 토큰을 별도로 처리할 수 있게 하고, 다른 JWT 예외들은 false를 반환하여 유효하지 않은 토큰으로 처리하는 전략이 적절합니다.

@Chuseok22 Chuseok22 merged commit 13925c6 into main Jan 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 새로운 기능 추가 (Feature)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant